#include "gc.h"

tiny_value *new_value(struct SecdVm *vm, enum tiny_type t)
{
	if (vm->count > GC_RUN_SIZE)
	{
		mark_all(vm);
		sweep(vm);
	}
	tiny_value *r = NULL;
	if (vm->freelist == NULL)
	{
		r = (tiny_value *)malloc(sizeof(tiny_value));
		r->tail = NULL;
    	r->length = 0;
    	r->next = NULL;
	}
	else
	{
		r = vm->freelist;
		vm->freelist = r->next;
		r->next = NULL;
	}

	if (vm->objects == NULL)
	{
		vm->objects = r;
		vm->current = r;
	}
	else
	{
		vm->current->next = r;
		vm->current = r;
	}
	++vm->count;
	r->type = t;
	r->mark = 1;
	//r->next = NULL;
	return r;
}

tiny_value *gc_make_integer(struct SecdVm *vm, int i)
{
	tiny_value *val = new_value(vm, tiny_type_integer);
	val->value.int_ = i;
	return val;
}
tiny_value *gc_make_double(struct SecdVm *vm, double v)
{
	tiny_value *val = new_value(vm, tiny_type_double);
	val->value.double_ = v;
	return val;
}
tiny_value *gc_make_bool(struct SecdVm *vm, int b)
{
	tiny_value *val = new_value(vm, tiny_type_bool);
	val->value.bool_ = b;
	return val;
}
tiny_value *gc_make_string(struct SecdVm *vm, char *s)
{
	tiny_value *val = new_value(vm, tiny_type_string);
	int len = strlen(s);
	val->value.str = (char *)malloc(len + 1);
	memset(val->value.str, 0, len + 1);
	memcpy(val->value.str, s, len);
	return val;
}
tiny_value *gc_make_symbol(struct SecdVm *vm, char *s)
{
	tiny_value *val = new_value(vm, tiny_type_symbol);
	int len = strlen(s);
	val->value.str = (char *)malloc(len + 1);
	memset(val->value.str, 0, len + 1);
	memcpy(val->value.str, s, len);
	return val;
}
tiny_value *gc_make_char(struct SecdVm *vm, char c)
{
	tiny_value *val = new_value(vm, tiny_type_char);
	val->value.char_ = c;
	return val;
}
tiny_value *gc_make_empty_list(struct SecdVm *vm){
	tiny_value *val = new_value(vm, tiny_type_list);
	return val;
}

tiny_value *gc_make_lambda(struct SecdVm *vm){
	tiny_value *val = new_value(vm, tiny_type_lambda);
	return val;
}

tiny_value *gc_make_func(struct SecdVm *vm,  c_func func){
	tiny_value *val = new_value(vm, tiny_type_procedure);
	val->value.procedure = func;
	return val;
}



tiny_value *gc_make_list(struct SecdVm *vm, tiny_value *list)
{
	tiny_value *val = new_value(vm, tiny_type_list);
	tiny_value *tmp = list->value.list;
	tiny_value *tmp1 = NULL;
	tiny_value *tmp2 = NULL;
	while (tmp != NULL)
	{
		switch (tmp->type)
		{
		case tiny_type_symbol:
			tmp1 = gc_make_symbol(vm, tmp->value.str);
			break;
		case tiny_type_string:
			tmp1 = gc_make_string(vm, tmp->value.str);
			break;
		case tiny_type_char:
			tmp1 = gc_make_char(vm, tmp->value.char_);
			break;
		case tiny_type_double:
			tmp1 = gc_make_double(vm, tmp->value.double_);
			break;
		case tiny_type_integer:
			tmp1 = gc_make_integer(vm, tmp->value.int_);
			break;
		case tiny_type_nil:
			tmp1 = tmp;
			break;
		case tiny_type_bool:
			tmp1 = gc_make_bool(vm, tmp->value.bool_);
			break;
		case tiny_type_list:
			tmp1 = gc_make_list(vm, tmp);
			break;
		default:
			tmp1 = tmp;
		}
		tmp = tmp->tail;
		if(tmp2 ==NULL){
			tmp2 = tmp1;
			val->value.list = tmp1;
		}else{
			tmp2->tail = tmp1;
		}
	}
	return val;
}

void free_value(struct SecdVm *vm, tiny_value *val)
{
	memset(val, 0, sizeof(tiny_value));
	val->next = vm->freelist;
	vm->freelist = val;
	--vm->count;
}

void mark_env(tiny_secd_env *val)
{
	tiny_value *t = NULL;
	tiny_secd_env *e = val;
	while (e != NULL)
	{
		int l = ListLength(e->local_table);
		while (l > 0)
		{
			--l;
			t = (tiny_value *)getItem(e->local_table, l);
			mark_value(t);
		}
		e = e->up_level;
	}
}

void mark_dump(tiny_secd_dump *d)
{

	tiny_value *t = NULL;
	if (d->e != NULL)
	{
		mark_env(d->e);
	}
	if (d->s != NULL)
	{
		int size = stackSize(d->s);
		tiny_value *val = NULL;
		while (size > 0)
		{
			--size;
			val = (tiny_value *)getPos(d->s, size);
			mark_value(val);
		}
	}
}

void mark_value(tiny_value *val)
{
	if(val == NULL){
		return ;
	}
	if(marked(val)){
		return ;
	}
	//printf("mark %p\n",val);
	tiny_value *t = NULL;
	val->mark = 1;
	tiny_secd_env *e = NULL;
	if (is_list(val))
	{
		t = val->value.list;
		while (t != NULL)
		{
			mark_value(t);
			t = t->tail;
		}
	}
	else if (is_lambda(val))
	{
		e = (tiny_secd_env *)val->value.env;
		mark_env(e);
	}
}

void mark_all(struct SecdVm *vm)
{
	printf("marking all...\n");
	tiny_value *val = vm->objects;
	while (val->next != NULL)
	{
		val->mark = 0;
		val = val->next;
	}
	//env stack dump lambda->env
	int size = stackSize(vm->stack);

	while (size > 0)
	{
		--size;
		val = (tiny_value *)getPos(vm->stack, size);
		mark_value(val);
	}
	mark_env(vm->environment);
	tiny_secd_dump *d = NULL;
	size = stackSize(vm->dump);
	while (size > 0)
	{
		--size;
		d = (tiny_secd_dump *)getPos(vm->dump, size);
		mark_dump(d);
	}
	long hash_long = 0;
	HNode *tmp = NULL;
	HNode *tmp_tmp = NULL;
	for (hash_long = 0; hash_long < Hash_Size; ++hash_long)
	{
		tmp = *(vm->globalEnv + hash_long);
		while (tmp != NULL)
		{
			tmp_tmp = tmp;
			tmp = tmp->next;
			mark_value(tmp_tmp->data);
		}
	}
}

void sweep(struct SecdVm *vm)
{
	int total = 0;
	int freed = 0;
	tiny_value *val = vm->objects;
	tiny_value *pre = NULL;
	while (val != NULL)
	{
		++total;
		if (!is_nil(val) && val->mark == 0)
		{
			vm->objects = val->next;
			free_value(vm,val);
			val = vm->objects;
			++freed;
		}
		else
		{
			if (pre == NULL)
			{
				pre = val;
			}
			else
			{
				pre->next = val;
				pre = val;
			}
			val = val->next;
		}
	}
	printf("gc end.\ntotal:%d\nfree:%d\nhave:%d\n", total, freed,vm->count);
}
